文章目录
前言函数描述代码实例如何得到客户端的IP 和 端口号
前言
当使用tcp服务器使用socket创建通信文件描述符,bind绑定了文件描述符,服务器ip和端口号,listen将服务器端的主动描述符转为被动描述符进行监听之后,接口accept通过三次握手与客户端建立连接
TCP 编程模型如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/2019101313284931.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly92aWdvdXJ0eXktemhnLmJsb2cuY3Nkbi5uZXQ=,size_16,color_FFFFFF,t_70)
函数描述
#include int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);函数功能: 被动监听客户端发起的tcp连接请求,三次握手后连接建立成功。客户端connect函数请求发起连接。 连接成功后服务器的tcp协议会记录客端的ip和端口,如果是跨网通信,记录ip的就是客户端所在路由器的公网ip返回值: 成功:返回一个通信描述符,专门用于与连接成功的客户端进行通信。 失败:返回-1 ,并设置errno函数参数: a. sockfd 已经被listen转为了被动描述符的“套接字文件描述符”,专门用于客户端的监听,入股sockfs没有被listen函数转为被动描述符,则accept是无法将其用来监听客户端连接的。 套接字文件描述符默认是阻塞的,即如果没有客户端请求连接的时候,此时accept会阻塞,直到有客户端连接;如果不想套接字文件描述符阻塞,则可以创建套接字 socket函数 时指定type为SOCK_NOBLOCK b. addrlen表示第二个参数addr的大小,不顾要求给定地址 c. addr: 用于记录发起连接请求的那个客户端的IP端口 建立连接时服务器的TCP协议会自动解析客户端发来的数据包,从中获取客户端的IP和端口号 这里如果服务器应用层需要用到客户端的 IP和端口号,可以给accept指定第二个参数addr,以获取TCP链接时的客户端ip和端口号;如果服务器应用层不需要,则写NULL即可 addr的结构体类型为 struct sockaddr,在listen函数详解中我们有介绍过,由于这个结构体用起来不是非常方便,我们需要定义struct sockaddr_in结构体来使得sockaddr结构体操作更为便捷。具体使用如下:struct sockaddr_in naddr = {0};
int nsize = sizeof(naddr);
int cfd = accept(sockfd, (struct sockaddr *)&naddr, &nsize);
代码实例
#include
#include
#include
#include
#include
#include
#include
#include
#include
void print_err(char *str, int line, int err_no) {
printf("%d, %s :%s\n",line,str,strerror(err_no));
_exit(-1);
}
int main()
{
int skfd = -1, ret = -1;
skfd = socket(AF_INET, SOCK_STREAM, 0);
if ( -1 == skfd) {
print_err("socket failed",__LINE__,errno);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET; //设置tcp协议族
addr.sin_port = 6789; //设置端口号
addr.sin_addr.s_addr = inet_addr("192.168.102.169"); //设置ip地址
ret = bind(skfd, (struct sockaddr*)&addr, sizeof(addr));
if ( -1 == ret) {
print_err("bind failed",__LINE__,errno);
}
/*将套接字文件描述符从主动转为被动文件描述符,然后用于被动监听客户端的连接*/
ret = listen(skfd, 3);
if ( -1 == ret ) {
print_err("listen failed", __LINE__, errno);
}
/*被动监听客户端发起的tcp连接请求,三次握手后连接建立成功*/
int cfd = -1;
struct sockaddr_in caddr = {0}; //为应用层获取客户端的IP和端口号
int csize = 0;
cfd = accept(skfd, (struct sockaddr*)&caddr, &csize);
if (-1 == cfd) {
print_err("accept failed", __LINE__, errno);
}
return 0;
}
如何得到客户端的IP 和 端口号
比如程序中想要打印客户端的ip和端口号,这里就需要使用到ntohs和inet_ntoa函数进行端序转换,因为客户端的端口和ip是服务器的TCP协议,从客户端发送端网络数据包中提取出来,网络数据包的端序属于网络端序,主机接收到数据后如果想要使用的话,就必须从网络端序转为主机端序。
举例如下:
struct sockaddr_in caddr = {0};
int csize = sizeof(caddr);
cfd = accept(sockfd, (struct sockaddr *)&caddr, &csize);
printf("cport = %d, caddr = %s\n", ntohs(caddr.sin_port),inet_ntoa(caddr.sin_addr));
|